using System;
using Godot;

namespace MyCraft2;

[Tool]
public partial class Cube : MeshInstance3D
{
    public const float PixelSize = 0.06f;

    [Export]
    public Texture2D? Texture
    {
        get => _standardMaterial3D.AlbedoTexture;
        set => _standardMaterial3D.AlbedoTexture = value;
    }

    [Export] public Vector3I MeshSize;
    [Export] public Vector2I TextureSize;

    [ExportGroup("UV")] [Export] public Vector2 FrontUv;
    [Export] public Vector2 BackUv;
    [Export] public Vector2 LeftUv;
    [Export] public Vector2 RightUv;
    [Export] public Vector2 TopUv;
    [Export] public Vector2 BottomUv;
    private static readonly int[] VertexOrder = [0, 1, 2, 0, 2, 3];

    private StandardMaterial3D _standardMaterial3D;

    public Cube()
    {
        _standardMaterial3D = new StandardMaterial3D();
        _standardMaterial3D.TextureFilter = BaseMaterial3D.TextureFilterEnum.Nearest;
        _standardMaterial3D.CullMode = BaseMaterial3D.CullModeEnum.Back;
        MaterialOverlay = _standardMaterial3D;
    }

    public override void _Ready()
    {
        var st = new SurfaceTool();
        st.Begin(Mesh.PrimitiveType.Triangles);

        var meshSize = new Vector3(MeshSize.X * PixelSize, MeshSize.Y * PixelSize, MeshSize.Z * PixelSize);
        var offset = meshSize / 2;
        for (var f = 0; f < 6; f++)
            foreach (var v in VertexOrder)
            {
                var vertices = offset;
                Vector2 uv;
                switch (f)
                {
                    case 0: // 前面
                        uv = FrontUv;
                        vertices.Z = -offset.Z;
                        if (v is 2 or 3)
                        {
                            vertices.X = -offset.X;
                            uv.X += MeshSize.X;
                        }

                        if (v is 0 or 3)
                        {
                            vertices.Y = -offset.Y;
                            uv.Y += MeshSize.Y;
                        }

                        break;
                    case 1: // 后面
                        uv = BackUv;
                        if (v is 0 or 1) vertices.X = -offset.X;
                        if (v is 0 or 3)
                        {
                            vertices.Y = -offset.Y;
                            uv.Y += MeshSize.Y;
                        }

                        if (v is 2 or 3) uv.X += MeshSize.X;
                        break;
                    case 2: // 左面
                        uv = LeftUv;
                        vertices.X = -offset.X;
                        if (v is 0 or 3)
                        {
                            vertices.Y = -offset.Y;
                            uv.Y += MeshSize.Y;
                        }

                        if (v is 0 or 1) vertices.Z = -offset.Z;
                        if (v is 2 or 3) uv.X += MeshSize.Z;
                        break;
                    case 3: // 右面
                        uv = RightUv;
                        if (v is 0 or 3)
                        {
                            vertices.Y = -offset.Y;
                            uv.Y += MeshSize.Y;
                        }

                        if (v is 2 or 3)
                        {
                            vertices.Z = -offset.Z;
                            uv.X += MeshSize.Z;
                        }

                        break;
                    case 4: // 上面
                        uv = TopUv;
                        if (v is 0 or 1) vertices.X = -offset.X;
                        if (v is 1 or 2) vertices.Z = -offset.Z;
                        if (v is 2 or 3) uv.X += MeshSize.X;
                        if (v is 0 or 3) uv.Y += MeshSize.Z;
                        break;
                    case 5: // 下面
                        uv = BottomUv;
                        vertices.Y = -offset.Y;
                        if (v is 0 or 1) vertices.X = -offset.X;
                        if (v is 0 or 3)
                        {
                            vertices.Z = -offset.Z;
                            uv.Y += MeshSize.Z;
                        }

                        if (v is 2 or 3) uv.X += MeshSize.X;
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }

                st.SetUV(uv / TextureSize);
                st.AddVertex(vertices);
            }

        st.GenerateNormals();
        var arrayMesh = new ArrayMesh();
        st.Commit(arrayMesh);
        Mesh = arrayMesh;
    }
}